/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2012-2019. All rights reserved.
 * Description: reset notify process
 * Author: huawei
 * Create: 2019-04-27
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/list.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/securec.h>
#include <linux/mm.h>
#include <linux/time.h>
#include "i2c_slave.h"

/*
    Master(peer)                       slave(local)
    tx req msg(0x20)-------------> set interrupt mask = 0( don't recv more)
                                   start timer
                                   ---set interrupt mask (start recv next)<-----write msg
                                   ---or timeout set interrupt mask

    tx rsp msg(0x21)------------->
                    <-------------ack msg(normal consumer's msg or timeout msg)
*/
/* buffer is share mem to user dmp */
typedef struct i2c_slave_st {
    MCU2MINI_QUERY_BLOCK *buffer;

    enum smbus_rx_status rx_status;
    unsigned int rd_size;
    unsigned char rd_buf[sizeof(struct smbus_req_msg) + I2C_SMBUS_BLOCK_MAX];
    unsigned char msg_buf[sizeof(struct smbus_rsp_msg) + I2C_SMBUS_BLOCK_MAX];
    unsigned int wr_size;
    unsigned char wr_buf[sizeof(struct smbus_rsp_msg) + I2C_SMBUS_BLOCK_MAX];

    struct list_head list;
    struct mutex lock;

    int status;
    unsigned int mask;

    dev_t devno;
    struct class *cls;
    struct device *node_device;
    struct cdev node_cdev;

    const char *dev_name;
    unsigned int sar;
    unsigned int s_hcnt;
    unsigned int s_lcnt;
    unsigned int sda_hold;
    phys_addr_t phybase;
    void __iomem *regs;
    int irq;
    struct platform_device *pdev;

    struct clk *clk;

    unsigned long stamp;
    unsigned int irq_cnt;
    unsigned int abrt_source;
    struct i2c_stat_st stat;
} I2C_SLAVE_ST;

#ifdef STATIC_SKIP
static int g_i2c_info_level = 1;
#else
static int g_i2c_info_level = 0;
#endif
unsigned int g_i2c_timeout = 300; /* ms */

module_param(g_i2c_timeout, int, 0640);
module_param(g_i2c_info_level, int, 0640);

STATIC int i2c_slave_tx(struct i2c_slave_st *hs, const unsigned char *buf, unsigned int len);

static LIST_HEAD(i2c_slave_head);

static const struct i2c_device_id i2c_slave_idtable[] = {
    { "i2c0_slave", 0 },
    { }
};

/*lint -e508 */
MODULE_DEVICE_TABLE(i2c, i2c_slave_idtable);
/*lint +e508 */

STATIC ssize_t show_debug_level(struct device *dev, struct device_attribute *attr, char *buf)
{
    if (buf == NULL) {
        return 0;
    }
    return scnprintf(buf, VALUE_BUF_LEN, "%d\n", g_i2c_info_level);
}

STATIC ssize_t set_debug_level(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    int value = 0;
    int ret;

    if ((count == 0) || (buf == NULL)) {
        return 0;
    }
    ret = kstrtoint(buf, DECIMAL, &value);
    if (ret != 0) {
        dev_info(dev, "set i2c_slave debug_level failed!\n");
        return count;
    }
    g_i2c_info_level = value;
    return count;
}

static DEVICE_ATTR(print_level, (S_IWUSR | S_IRUGO), show_debug_level, set_debug_level);

STATIC void i2c_slave_add_hs(struct i2c_slave_st *hs)
{
    list_add_tail(&hs->list, &i2c_slave_head);
}

STATIC struct i2c_slave_st *i2c_slave_get_hs(struct cdev *dev)
{
    struct i2c_slave_st *hs = NULL;
    struct list_head *pos = NULL;
    struct list_head *n = NULL;

    list_for_each_safe(pos, n, &i2c_slave_head)
    {
        hs = list_entry(pos, struct i2c_slave_st, list);
        dev_info(&hs->pdev->dev, "get hs, %pK, %pK, %pK\n", dev, &hs->node_cdev, hs);
        if (&hs->node_cdev == dev) {
            return hs;
        }
    }
    return NULL;
}

STATIC void i2c_slave_enable(struct i2c_slave_st *hs)
{
    i2c_writel(hs, ENABLE, 1);
    dev_info(&hs->pdev->dev, "set i2c enable, %x\n", i2c_readl(hs, ENABLE));
}

STATIC void i2c_slave_disable(struct i2c_slave_st *hs)
{
    i2c_writel(hs, ENABLE, 0);
    dev_info(&hs->pdev->dev, "set i2c disable, %x\n", i2c_readl(hs, ENABLE));
}

STATIC int i2c_slave_activity(struct i2c_slave_st *hs)
{
    unsigned int val = i2c_readl(hs, STATUS);
    return I2C_BFEXT(ACTIVITY, val);
}

STATIC unsigned int i2c_slave_tx_fifo_status(struct i2c_slave_st *hs)
{
    unsigned int val = i2c_readl(hs, STATUS);

    if (I2C_BFEXT(TFE, val) != 0) {
        return I2C_SEND_FIFO_EMPTY;
    } else {
        return I2C_SEND_FIFO_NOT_EMPTY;
    }
}

STATIC unsigned int i2c_slave_rx_fifo_status(struct i2c_slave_st *hs)
{
    unsigned int val = i2c_readl(hs, STATUS);
    /* the fifo non-empty status */
    if (I2C_BFEXT(RFNE, val) == 0) {
        return I2C_RECV_FIFO_EMPTY;
    } else {
        return I2C_RECV_FIFO_NOT_EMPTY;
    }
}

STATIC void i2c_slave_timeout_msg(struct i2c_slave_st *hs)
{
    int i;
    MCU2MINI_RESP_DATA *msg = NULL;

    msg = (MCU2MINI_RESP_DATA *)&hs->msg_buf[1];

    if (msg->data_length > (I2C_SMBUS_BLOCK_MAX - I2C_SMBUS_HEAD_LEN)) {
        msg->data_length = I2C_SMBUS_BLOCK_MAX - I2C_SMBUS_HEAD_LEN;
        dev_err(&hs->pdev->dev,
            "revcive len is error, opcode:%x, recv pks = %u, timeout msg = %u, rx_status=%u, write=%u, msg len=%d\n",
            msg->opcode, hs->stat.rx_pks, hs->stat.timeout_msg, hs->rx_status, hs->stat.fs_write, msg->data_length);
    }
    /* data len */
    hs->msg_buf[0] = I2C_SMBUS_HEAD_LEN + msg->data_length;
    /* Internal Error */
    msg->err_code = OTHER_ERROR;
    msg->total_length = msg->data_length;

    for (i = 0; i < DATA_MAX_LEN; i++) {
        msg->data[i] = 0;
    }
    /* +1 is add msg_buf[0] */
    (void)i2c_slave_tx(hs, (const unsigned char *)hs->msg_buf, BUF_LEN_BYTE + I2C_SMBUS_HEAD_LEN + msg->data_length);

    hs->stat.timeout_msg++;

    dev_info(&hs->pdev->dev,
        "timeout and clear interrupt, opcode:%x, recv pks = %u, timeout msg = %u, rx_status=%u, write=%u, msg len=%d\n",
        msg->opcode, hs->stat.rx_pks, hs->stat.timeout_msg, hs->rx_status, hs->stat.fs_write, msg->data_length);

    (void)i2c_readl(hs, CLR_INTR);

    return;
}

STATIC int i2c_slave_tx(struct i2c_slave_st *hs, const unsigned char *buf, unsigned int len)
{
    unsigned int i;
    int ret = 0;
    unsigned int val;
    unsigned int val0;
    unsigned int raw_intr;
    unsigned int intr;
    int log = 0;

    SLAVE_I2C_OUT(&hs->pdev->dev, "i2c slave send,len:%d\n", len);
    for (i = 0; i < len; i++) {
        val = (unsigned int)buf[i];
        i2c_writel(hs, DATA_CMD, val);

        SLAVE_I2C_OUT(&hs->pdev->dev, "%02x ", buf[i]);
        udelay(25); /* 25us. 10 times cycle(400khz) */
    }
    SLAVE_I2C_OUT(&hs->pdev->dev, "\n");

    /* on 100khz speed. send 32Byte need: 32*90us(1bit need 10us, 8bit+1bit ack) */
    for (i = 0; i < I2C_WR_TIMEOUT; i++) {
        udelay(2);
        if (i2c_slave_tx_fifo_status(hs) == I2C_SEND_FIFO_EMPTY) {
            break;
        }

        if (log == 0 && g_i2c_info_level) {
            log++;

            val0 = i2c_readl(hs, STATUS);
            intr = i2c_readl(hs, INTR_STAT);
            raw_intr = i2c_readl(hs, RAW_INTR_STAT);
            val = i2c_readl(hs, TX_ABRT_SOURCE);
            SLAVE_I2C_OUT(&hs->pdev->dev,
                "send %pK(len %d) again. abort %u,status %u, intr 0x%x, raw intr 0x%x, err cnt %u.", buf, len, val,
                val0, intr, raw_intr, hs->stat.tx_err);
        }
    }
    if (i == I2C_WR_TIMEOUT) {
        hs->stat.tx_err++;
        val0 = i2c_readl(hs, STATUS);
        intr = i2c_readl(hs, INTR_STAT);
        raw_intr = i2c_readl(hs, RAW_INTR_STAT);
        val = i2c_readl(hs, TX_ABRT_SOURCE);
        dev_info(&hs->pdev->dev, "tx %pK(len %d) timeout. abort %u,status %u, intr 0x%x, raw intr 0x%x, err cnt %u.",
            buf, len, val, val0, intr, raw_intr, hs->stat.tx_err);
    } else {
        hs->stat.tx_total += len;
    }

    return ret;
}

STATIC int i2c_slave_tx_req(struct i2c_slave_st *hs)
{
    if (hs->wr_size) {
        /* hs->wr_buf(no count) and hs->size recive from dmp */
        hs->wr_size =
            (hs->wr_size < (I2C_SMBUS_BLOCK_MAX + BUF_LEN_BYTE)) ? hs->wr_size : (I2C_SMBUS_BLOCK_MAX + BUF_LEN_BYTE);
        (void)i2c_slave_tx(hs, (const unsigned char *)hs->wr_buf, (unsigned int)hs->wr_size);
    } else {
        dev_info(&hs->pdev->dev, "tx response len is error\n");
        i2c_slave_timeout_msg(hs);
    }

    return 0;
}

STATIC int i2c_slave_rx_smbus(struct i2c_slave_st *hs, unsigned char *msg, unsigned int len)
{
    unsigned int i;
    unsigned int j;
    int ret;

    for (i = 0; i < len; i++) {
        switch (hs->rx_status) {
            case SRS_REQ: {
                if (msg[i] == I2C_SMBUS_WR_REQ) {
                    hs->rd_buf[0] = I2C_SMBUS_WR_REQ;
                    hs->rd_size = 1;
                    hs->rx_status = SRS_CNT;
                }
                break;
            }
            case SRS_CNT: {
                if (msg[i] <= I2C_SMBUS_BLOCK_MAX) {
                    hs->rd_buf[1] = msg[i];
                    hs->rd_size = 2;
                    hs->rx_status = SRS_BODY;
                } else {
                    hs->rx_status = SRS_REQ;
                    dev_info(&hs->pdev->dev, "recv invalid count:0x%02x\n", msg[i]);
                }
                break;
            }
            case SRS_BODY: {
                /* 2 = cmd + data len */
                if (hs->rd_size < (unsigned char)hs->rd_buf[1] + 2) {
                    hs->rd_buf[hs->rd_size] = msg[i];
                    hs->rd_size++;
                    hs->stat.rx_total++;
                }
                /* receive last byte */
                if (hs->rd_size == (unsigned char)hs->rd_buf[1] + 2) {
                    hs->stat.rx_pks++;
                    for (j = 0; j < sizeof(hs->msg_buf); j++) {
                        hs->msg_buf[j] = 0;
                    }
                    ret = memcpy_s(&hs->msg_buf[1], sizeof(hs->msg_buf) - 1, &hs->rd_buf[2], hs->rd_buf[1]);
                    ASSERT_RET((ret == 0), 0);

                    /* init wr_size when ask dmp to fill data */
                    hs->wr_size = 0;
                    hs->rx_status = SRS_REQ;

                    if (i + 1 != len) {
                        /* drop other bytes */
                        hs->stat.rx_drop++;
                    }
                    /* finish receive from mcu */
                    return BUS_RX_FIN;
                }
                break;
            }
            default:
                hs->rx_status = SRS_REQ;
                break;
        }
    }

    return 0;
}

STATIC int seek_item_data(struct i2c_slave_st *hs, unsigned char *item_location)
{
    int i;
    int j;
    unsigned char type_flag;
    unsigned short dmp_opcode;
    MCU2MINI_QUERY_BLOCK *mem_from_usr = NULL;
    MCU2MINI_REQ_DATA *req_data = NULL;

    mem_from_usr = (MCU2MINI_QUERY_BLOCK *)hs->buffer;
    req_data = (MCU2MINI_REQ_DATA *)&hs->msg_buf[1];

    for (i = 0; i < MAX_ITEM_LEN; i++) {
        type_flag = 1;
        dmp_opcode = (unsigned short)((mem_from_usr->item_info[i].op_fun << 8) | mem_from_usr->item_info[i].op_cmd);
        if (req_data->opcode == dmp_opcode) {
            if (mem_from_usr->item_info[i].type_len > MAX_TPYE_LEN) {
                dev_err(&hs->pdev->dev, "the item=%d(opcode=0x%x) type len is error.\n", i, req_data->opcode);
                continue;
            }

            if ((mem_from_usr->item_info[i].type_len > 0) && (mem_from_usr->item_info[i].type_len <= MAX_TPYE_LEN)) {
                type_flag = 0;
                for (j = 0; j < mem_from_usr->item_info[i].type_len; j++) {
                    if (req_data->data[j] != mem_from_usr->item_info[i].arg[j]) {
                        SLAVE_I2C_OUT(&hs->pdev->dev,
                            "seek j=%d, i=%d, type_len=%d, req_data->data[j]=0x%x, item_info[i].arg[j]=0x%x.\n", j, i,
                            mem_from_usr->item_info[i].type_len, req_data->data[j], mem_from_usr->item_info[i].arg[j]);
                        type_flag = 0;
                        continue;
                    }
                    type_flag = 1;
                }
            }

            if (type_flag && mem_from_usr->item_info[i].valid) {
                if (req_data->offset > 0) {
                    if (req_data->offset != mem_from_usr->item_info[i].offset) {
                        SLAVE_I2C_OUT(&hs->pdev->dev, "the item=%d(opcode=0x%x) offset is error.\n", i,
                            req_data->opcode);
                        continue;
                    }
                }
                *item_location = i;
                SLAVE_I2C_OUT(&hs->pdev->dev, "find the item=%d(opcode=0x%x) data.\n", i, req_data->opcode);
                return 0;
            } else if (!type_flag) {
                SLAVE_I2C_OUT(&hs->pdev->dev, "the item=%d(opcode=0x%x) data not find, type_flag=%d.\n", i,
                    req_data->opcode, type_flag);
                continue;
            } else {
                *item_location = i;
                SLAVE_I2C_OUT(&hs->pdev->dev, "the item=%d(opcode=0x%x) data is invaild, type_flag=%d.\n", i,
                    req_data->opcode, type_flag);
                return INVALID;
            }
        }
    }

    *item_location = i;
    return NOT_FIND;
}

STATIC int fill_request_data_buf(struct i2c_slave_st *hs)
{
    int ret;
    unsigned int i;
    unsigned char item_location = 0;
    MCU2MINI_QUERY_BLOCK *mem_from_usr = NULL;
    MCU2MINI_REQ_DATA *req_data = NULL;
    MCU2MINI_RESP_DATA *resp_data = NULL;
    struct timespec64 tv = { 0 };

    /* will set 0 when consume in i2c_slave_tx_req and init the send buf */
    if (hs->wr_size) {
        dev_err(&hs->pdev->dev, "Already have data need send,wr_size:%d\n", hs->wr_size);
        return -EBUSY;
    }

    mem_from_usr = (MCU2MINI_QUERY_BLOCK *)hs->buffer;
    /* receive data from mcu */
    req_data = (MCU2MINI_REQ_DATA *)&hs->msg_buf[1];

    SLAVE_I2C_OUT(&hs->pdev->dev, "request opcode:0x%x, len:0x%x, data:0x%02x\n", req_data->opcode, req_data->length,
        req_data->data[0]);

    /* response data to mcu */
    resp_data = (MCU2MINI_RESP_DATA *)&hs->wr_buf[1];
    /* data len */
    hs->wr_buf[0] = (char)(I2C_SMBUS_HEAD_LEN + req_data->length);
    /* data + data_len */
    hs->wr_size = (unsigned int)(hs->wr_buf[0] + BUF_LEN_BYTE);

    resp_data->opcode = req_data->opcode;
    resp_data->total_length = req_data->length;
    resp_data->data_length = req_data->length;

    for (i = 0; i < DATA_MAX_LEN; i++) {
        resp_data->data[i] = 0;
    }

    ktime_get_real_ts64(&tv);
    if ((tv.tv_sec - mem_from_usr->updata_time) > BLOCK_TIMEOUT ||
        (mem_from_usr->updata_time - tv.tv_sec) > BLOCK_TIMEOUT) {
        resp_data->err_code = TIMEOUT;
        SLAVE_I2C_OUT(&hs->pdev->dev, "data(opcode=0x%x) is timeout, dmp time:%ld, kernel time:%ld.\n",
            req_data->opcode, mem_from_usr->updata_time, tv.tv_sec);
        return 0;
    }

    ret = seek_item_data(hs, &item_location);
    if (ret != 0) {
        resp_data->err_code = ret;
        SLAVE_I2C_OUT(&hs->pdev->dev, "seek the item(opcode=0x%x) data error item=%d.\n", req_data->opcode,
            item_location);
        return 0;
    }

    resp_data->err_code = 0;
    resp_data->total_length = mem_from_usr->item_info[item_location].total_length;

    if (mem_from_usr->item_info[item_location].length > DATA_MAX_LEN) {
        resp_data->err_code = DIFF_LEN;
        dev_err(&hs->pdev->dev, "item:%d,len is error, req_data->length=%d, item_info[%d].len=%d\n", item_location,
            req_data->length, i, mem_from_usr->item_info[item_location].length);
        return 0;
    }

    for (i = 0; i < mem_from_usr->item_info[item_location].length; i++) {
        resp_data->data[i] = mem_from_usr->item_info[item_location].data[i];
        SLAVE_I2C_OUT(&hs->pdev->dev, "fill data[%d]=0x%x \n", i, mem_from_usr->item_info[item_location].data[i]);
    }
#ifndef STATIC_SKIP
    hs->stat.fs_write++;
#endif
    return 0;
}

STATIC int i2c_slave_rx_req(struct i2c_slave_st *hs)
{
    unsigned int val;
    unsigned int i;
    unsigned int len;
    unsigned int rx_len;
    unsigned char rx_buf[sizeof(struct smbus_req_msg) + I2C_SMBUS_BLOCK_MAX] = {0};

    if (i2c_slave_rx_fifo_status(hs) == I2C_RECV_FIFO_EMPTY) {
        dev_err(&hs->pdev->dev, "Rx fifo is empty, i2c status:0x%x.\n", i2c_readl(hs, STATUS));
        return -ETIME;
    }

    /* number of receive data in fifo, current is 1 */
    len = i2c_readl(hs, RXFLR);
    rx_len = len > sizeof(rx_buf) ? sizeof(rx_buf) : len;

    SLAVE_I2C_OUT(&hs->pdev->dev, "recv len:%u, Rx data:\n", len);

    for (i = 0; i < rx_len; i++) {
        udelay(1);
        /* clear r_rx_full after read */
        val = i2c_readl(hs, DATA_CMD);
        rx_buf[i] = I2C_BFEXT(DAT, val) & 0xff;
        SLAVE_I2C_OUT(&hs->pdev->dev, "0x%x ", (unsigned char)val);
    }

    /* fill hs->rxbuf */
    return i2c_slave_rx_smbus(hs, rx_buf, rx_len);
}

STATIC int i2c_slave_hw_init(struct i2c_slave_st *hs)
{
    unsigned int val;
    void __iomem *regs = NULL;
    int i;

    for (i = 0; i < I2C_INIT_TIMEO; i++) {
        udelay(1);
        if (i2c_slave_activity(hs) == 0) {
            break;
        }
    }
    if (i == I2C_INIT_TIMEO) {
        dev_err(&hs->pdev->dev, "get idle status timeout, status:0x%08x.\n", i2c_readl(hs, STATUS));
    }

    i2c_slave_disable(hs);

    if (i2c_slave_activity(hs) != 0) {
        dev_err(&hs->pdev->dev, "i2c control is not idle, status:0x%08x.\n", i2c_readl(hs, STATUS));
        return -EBUSY;
    }

    /* map the phys addr to virtural addr(3-4GB) */
    regs = ioremap(MULTI_REGS_I2C, SHARE_MEM_PAGE);
    if (regs == NULL) {
        dev_err(&hs->pdev->dev, "remap mem failed.\n");
        return -EBUSY;
    }

    writel_relaxed(0x2, regs + 0x10);
    writel_relaxed(0x2, regs + 0x14);

    val = i2c_readl(hs, CON);                                            /* read contorl register */
    val = I2C_BFINS(SLAVE_DISABLE, 0, val); /* ENABLE slave mode */      /*lint !e502*/
    val = I2C_BFINS(MASTER, 0, val); /* DISABLE master mode */           /*lint !e502*/
    val = I2C_BFINS(SPEDD, (unsigned int)0x1, val); /* standard speed */ /*lint !e502*/

    i2c_writel(hs, CON, val);
    /* source addr */
    i2c_writel(hs, SAR, hs->sar);

    val = i2c_readl(hs, SAR);
    dev_info(&hs->pdev->dev, "read SAR value: %x\n", val);

    /* i2c-sda falling time */
    i2c_writel(hs, SS_SCL_HCNT, hs->s_hcnt);
    i2c_writel(hs, SS_SCL_LCNT, hs->s_lcnt);
    i2c_writel(hs, SDA_HOLD, I2C_SLAVE_HOLD_VALUE);

    i2c_writel(hs, RX_TL, 0);
    i2c_writel(hs, TX_TL, 0);

    dev_info(&hs->pdev->dev, "write mask: %s\n", "0x0066");

    i2c_writel(hs, INTR_MASK, I2C_SLAVE_INTR_MASK);

    hs->mask = I2C_SLAVE_INTR_MASK;

    iounmap(regs);

    return 0;
}

void clear_fifo_data(struct i2c_slave_st *hs)
{
    int len;
    int i;

    len = i2c_readl(hs, RXFLR);
    for (i = 0; i < len; i++) {
        udelay(1);
        (void)i2c_readl(hs, DATA_CMD);
    }
}

STATIC irqreturn_t i2c_slave_interrupt(int irq, void *dev_id)
{
    struct i2c_slave_st *hs = dev_id;
    unsigned int intr;
    unsigned int raw_intr;
    unsigned int status;
    unsigned int r_rd_req;
    unsigned int r_rx_full;
    unsigned int r_rx_over;
    int ret;
    unsigned int mask;

    hs->irq_cnt++;
    mask = i2c_readl(hs, INTR_MASK);
    intr = i2c_readl(hs, INTR_STAT);
    raw_intr = i2c_readl(hs, RAW_INTR_STAT);
    status = i2c_readl(hs, STATUS);

    i2c_writel(hs, INTR_MASK, 0);

    if (g_i2c_info_level) {
        dev_info(&hs->pdev->dev,
            "Enter irq:0x%x(%d) mask:0x%08x, status:0x%08x,intr:0x%08x,raw_intr:0x%08x,abrt:0x%08x\n", irq, hs->irq_cnt,
            mask, status, intr, raw_intr, hs->stat.tx_abrt);
    }

    if (I2C_BFEXT(R_TX_ABRT, intr)) {
        hs->abrt_source = i2c_readl(hs, TX_ABRT_SOURCE);
        SLAVE_I2C_OUT(&hs->pdev->dev, "R_TX_ABRT tx_abrt:%d, abrt_source:0x%08x.\n", hs->stat.tx_abrt, hs->abrt_source);
        (void)i2c_readl(hs, CLR_TX_ABRT);
        hs->stat.tx_abrt++;
    }

    /* the status of receive interrupt */
    r_rd_req = I2C_BFEXT(R_RD_REQ, intr);
    if (r_rd_req) {
        hs->stat.tx_req++;
        (void)i2c_slave_tx_req(hs);
        /* clear the status of I2C_RAW_INTR_STAT[rd_req] */
        (void)i2c_readl(hs, CLR_RD_REQ);
    }

    // the threshold is I2C_RX_TL: 1
    r_rx_full = I2C_BFEXT(R_RX_FULL, intr);

    /* clear the bit after read the data int DATA_CMD(rx fifo) */
    if (r_rx_full) {
        ret = i2c_slave_rx_req(hs);
        if (ret == BUS_RX_FIN) {
            (void)fill_request_data_buf(hs);
        }
        hs->stat.rx_full += r_rx_full;
    }

    /* lose data */
    r_rx_over = I2C_BFEXT(R_RX_OVER, intr);
    if (r_rx_over) {
        SLAVE_I2C_OUT(&hs->pdev->dev, "R_RX_OVER intr_stat:0x%08x, i2c status:0x%08x, stat.rx_over:0x%x.\n", intr,
            status, hs->stat.rx_over);
        clear_fifo_data(hs);
        (void)i2c_readl(hs, CLR_INTR);
        hs->stat.rx_over += r_rx_over;
    }

    i2c_writel(hs, INTR_MASK, I2C_SLAVE_INTR_MASK);

    return IRQ_HANDLED;
}


STATIC int i2c_slave_smbus_open(struct inode *node, struct file *filep)
{
    struct i2c_slave_st *hs = NULL;

    if (node == NULL || filep == NULL) {
        return -ENODEV;
    }

    hs = i2c_slave_get_hs(node->i_cdev);
    if (hs == NULL) {
        return -ENODEV;
    }

    mutex_lock(&hs->lock);

    if (hs->status == I2C_SLAVE_BUSY) {
        mutex_unlock(&hs->lock);
        dev_err(&hs->pdev->dev, "i2c slave is busy\n");
        return -EBUSY;
    }

    hs->status = I2C_SLAVE_BUSY;
    filep->private_data = hs;
    i2c_slave_enable(hs);

    mutex_unlock(&hs->lock);

    return 0;
}

STATIC int i2c_slave_smbus_release(struct inode *node, struct file *filep)
{
    struct i2c_slave_st *hs = NULL;

    if ((node == NULL) || (filep == NULL) || (filep->private_data == NULL)) {
        return -ENODEV;
    }

    hs = (struct i2c_slave_st *)filep->private_data;

    mutex_lock(&hs->lock);

    if (hs->status == I2C_SLAVE_BUSY) {
        hs->status = I2C_SLAVE_IDLE;
    }
    hs->rd_size = 0;
    i2c_slave_disable(hs);
    filep->private_data = NULL;

    mutex_unlock(&hs->lock);

    dev_info(&hs->pdev->dev, "i2c slave release\n");
    return 0;
}

STATIC int i2c_slave_smbus_mmap(struct file *filep, struct vm_area_struct *vm_area)
{
    int ret;
    unsigned long page;
    unsigned long start;
    unsigned long size;
    struct i2c_slave_st *hs = NULL;

    if ((filep == NULL) || (vm_area == NULL) || (filep->private_data == NULL)) {
        return -EINVAL;
    }

    hs = (struct i2c_slave_st *)filep->private_data;

    size = (unsigned long)(vm_area->vm_end - vm_area->vm_start);
    start = (unsigned long)vm_area->vm_start;
    page = virt_to_phys(hs->buffer);

    ASSERT_RET((size <= SHARE_MEM_LEN), -EINVAL);

    ret = remap_pfn_range(vm_area, start, page >> PAGE_SHIFT, size, vm_area->vm_page_prot);
    if (ret) {
        dev_err(&hs->pdev->dev, "remap_pfn_range error ret=%d\n", ret);
        return -EINVAL;
    }
    dev_info(&hs->pdev->dev, "mmap success\n");
    return 0;
}


STATIC struct file_operations i2c_slave_fops = {
    .owner = THIS_MODULE,
    .open = i2c_slave_smbus_open,
    .release = i2c_slave_smbus_release,
    .mmap = i2c_slave_smbus_mmap,
};

STATIC int i2c_slave_cdev_init(struct i2c_slave_st *hs)
{
    int ret;
    dev_t devno;

    /* register device num */
    ret = alloc_chrdev_region(&devno, 0, 1, I2C0_DEV_NAME);
    if (ret < 0) {
        dev_err(&hs->pdev->dev, "%s: alloc_chrdev_region fail! ret = %d\n", I2C0_DEV_NAME, ret);
        return ret;
    }
    hs->devno = devno;
    hs->cls = class_create(THIS_MODULE, I2C0_DEV_NAME);
    if (IS_ERR(hs->cls)) {
        ret = PTR_ERR(hs->cls);
        dev_err(&hs->pdev->dev, "class create error %d\n", ret);
        goto init_err1;
    }

    hs->node_device = device_create(hs->cls, NULL, devno, NULL, I2C0_DEV_NAME); /* create node of device */
    if (IS_ERR(hs->node_device)) {
        ret = -EPERM;
        dev_err(&hs->pdev->dev, "device create error!\n");
        goto init_err2;
    }

    cdev_init(&hs->node_cdev, &i2c_slave_fops);
    hs->node_cdev.owner = THIS_MODULE;
    ret = cdev_add(&hs->node_cdev, devno, 1);
    if (ret) {
        dev_err(&hs->pdev->dev, "Error %d adding node_cdev!\n", ret);
        goto init_err3;
    }

    return 0;

init_err3:
    device_destroy(hs->cls, devno);
init_err2:
    class_destroy(hs->cls);
init_err1:
    unregister_chrdev_region(devno, 1);
    return ret;
}

STATIC void i2c_slave_cdev_exit(struct i2c_slave_st *hs)
{
    device_destroy(hs->cls, hs->devno);
    class_destroy(hs->cls);
    unregister_chrdev_region(hs->devno, 1);
    cdev_del(&hs->node_cdev);
}

/*
 * @np:         device node from which the property value is to be read.
 * @propname:   name of the property to be searched.
 * @out_values:   pointer to return value, modified only if return value is 0.
 * @sz:        number of array elements to read
 *
 */
STATIC int init_i2c_dev(struct platform_device *pdev, struct i2c_slave_st *hs)
{
    int ret;
    int irq;
    const char *name = NULL;
    unsigned int sar = 0;
    unsigned int timeout = 0;
    unsigned int sda_fall = 0;
    unsigned int scl_fall = 0;
    unsigned int sda_hold = 0;
    void __iomem *i2c_slv_trap_reg = NULL;
    unsigned int pad_i2c_slv_addr = 0;
    struct clk *clk = NULL;
    struct resource *res = NULL;
    struct device_node *np = pdev->dev.of_node;

    ret = of_property_read_u32(np, "sar-addr", &sar);
    dev_info(&pdev->dev, "get sar: 0x%x, ret %d\n", sar, ret);
    ASSERT_RET((ret == 0), -EINVAL);

    i2c_slv_trap_reg = ioremap(I2C_SLV_BASE_ADDR, SHARE_MEM_PAGE * PAGE_NUM);
    if (i2c_slv_trap_reg == NULL) {
        dev_err(&pdev->dev, "i2c slave addr map failed.\n");
        return -EINVAL;
    }
    pad_i2c_slv_addr = readl_relaxed(i2c_slv_trap_reg + PAD_I2C_OFFSET);
    dev_info(&pdev->dev, "i2c slave reg: 0x%x, HW addr=0x%x\n", pad_i2c_slv_addr,
        ((pad_i2c_slv_addr >> TRAP_ADDR_BIT) & HW_ADDR_BIT));

    sar = (sar & LOW_BIT_MASK) | ((pad_i2c_slv_addr >> TRAP_ADDR_BIT) & HW_ADDR_BIT);
    dev_info(&pdev->dev, "set sar: 0x%x\n", sar);
    iounmap(i2c_slv_trap_reg);

    ret = of_property_read_u32(np, "i2c-timeout-ms", &timeout);
    if ((ret != 0) || (timeout <= 0)) {
        timeout = g_i2c_timeout;
    }
    dev_info(&pdev->dev, "i2c-timeout-ms %u\n", timeout);

    ret = of_property_read_u32(np, "i2c-sda-falling-time-ns", &sda_fall);
    dev_info(&pdev->dev, "i2c-sda-falling-time-ns: 0x%x, ret %d\n", sda_fall, ret);
    ASSERT_RET((ret == 0), -EINVAL);

    ret = of_property_read_u32(np, "i2c-scl-falling-time-ns", &scl_fall);
    dev_info(&pdev->dev, "i2c-scl-falling-time-ns: 0x%x, ret %d\n", scl_fall, ret);
    ASSERT_RET((ret == 0), -EINVAL);

    ret = of_property_read_string(np, "dev-name", &name);
    dev_info(&pdev->dev, "get dev-name: %s, ret %d\n", name, ret);
    ASSERT_RET((ret == 0), -EINVAL);

    /* get base addr */
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (unlikely(res == NULL)) {
        dev_err(&pdev->dev, "invalid resource\n");
        return -EINVAL;
    }

    /* get an IRQ for a device */
    irq = platform_get_irq(pdev, 0);
    if (irq < 0) {
        dev_err(&pdev->dev, "platform_get_irq error\n");
        return -ENODEV;
    }
    dev_info(&pdev->dev, "get irq %d\n", irq);

    clk = clk_get(&pdev->dev, "i2c_clk");
    if (IS_ERR(clk)) {
        return PTR_ERR(clk);
    }

    dev_info(&pdev->dev, "clk_get\n");

    /* set usr struct datat to pdev->device_private->driver_data */
    platform_set_drvdata(pdev, hs);
    /* init list */
    INIT_LIST_HEAD(&hs->list);

    hs->regs = ioremap(res->start, resource_size(res));
    if (hs->regs == NULL) {
        dev_err(&pdev->dev, "ioremap error.\n");
        return -ENOMEM;
    }

    hs->status = I2C_SLAVE_IDLE;

    hs->dev_name = name; /* 110130000.i2c */
    hs->sar = sar;       /* source addr */
    hs->phybase = res->start;
    hs->pdev = pdev;
    hs->clk = clk;
    hs->s_hcnt = (100000 * (4000 + scl_fall) + 500000) / 1000000 - 3 + 0;
    hs->s_lcnt = ((100000 * (4700 + sda_fall) + 500000) / 1000000) - 1 + 0;
    hs->sda_hold = (sda_hold * 100 + 500000) / 100000;

    hs->rx_status = SRS_REQ;
    hs->irq = irq;

    dev_info(&pdev->dev, "hs->s_hcnt: 0x%x\n", hs->s_hcnt);
    dev_info(&pdev->dev, "hs->s_lcnt: 0x%x\n", hs->s_lcnt);
    dev_info(&pdev->dev, "hs->sda_hold: 0x%x\n", hs->sda_hold);

    return 0;
}

STATIC int i2c_slave_probe(struct platform_device *pdev)
{
    int ret;
    struct i2c_slave_st *hs = NULL;

    dev_info(&pdev->dev, "g_i2c_info_level:%d, g_i2c_timeout:%d\n", g_i2c_info_level, g_i2c_timeout);
    dev_info(&pdev->dev, "i2c name %s, id:%d, auto:%d, num res:%u\n", pdev->name, pdev->id, pdev->id_auto,
        pdev->num_resources);

    hs = kzalloc(sizeof(*hs), GFP_KERNEL);
    if (hs == NULL) {
        dev_err(&pdev->dev, "kmalloc error.\n");
        ret = -ENOMEM;
        goto out_kmalloc;
    }

    ret = init_i2c_dev(pdev, hs);
    if (ret) {
        dev_err(&pdev->dev, "init_i2c_dev ret=%d.\n", ret);
        goto out_kfree;
    }

    mutex_init(&hs->lock);

    /* register interrput, and hs is in arg */
    ret = request_irq(hs->irq, i2c_slave_interrupt, 0, dev_name(&pdev->dev), hs);
    if (ret) {
        dev_err(&pdev->dev, "request_irq error %d.\n", ret);
        goto out_irq;
    }

    ret = i2c_slave_hw_init(hs);
    if (ret) {
        dev_err(&pdev->dev, "i2c_hw_init error ret=%d.\n", ret);
        goto out_cdev;
    }

    SLAVE_I2C_OUT(&pdev->dev, "i2c_hw_init. hs=%pK\n", hs);

    ret = i2c_slave_cdev_init(hs);
    if (ret) {
        dev_info(&pdev->dev, "i2c_cdev_init failed %d\n", ret);
        goto out_cdev;
    }

    i2c_slave_add_hs(hs);

    if (device_create_file(&pdev->dev, &dev_attr_print_level)) {
        dev_info(&pdev->dev, "Could not create sysfs file for dev_attr_debug_level\n");
    }

    hs->buffer = (MCU2MINI_QUERY_BLOCK *)kzalloc(SHARE_MEM_LEN, GFP_KERNEL);
    if (hs->buffer == NULL) {
        ret = -EPERM;
        dev_err(&pdev->dev, "kzalloc buffer error\n");
        goto out_cdev;
    }
    /*lint -e648 */
    SetPageReserved((struct page *)(uintptr_t)(virt_to_page((unsigned long)(uintptr_t)hs->buffer)));
    /*lint +e648 */
    dev_info(&pdev->dev, "i2c slave probe success and malloc len=%d\n", SHARE_MEM_LEN);
    return 0;

out_cdev:
    free_irq(hs->irq, hs);
out_irq:
    iounmap(hs->regs);
    hs->regs = NULL;
out_kfree:
    kfree(hs);
    hs = NULL;
out_kmalloc:

    return ret;
}

STATIC int i2c_slave_remove(struct platform_device *pdev)
{
    struct i2c_slave_st *hs = platform_get_drvdata(pdev);
    if (hs == NULL) {
        return 0;
    }
    dev_info(&pdev->dev, "remove i2c0_slave\n");

    i2c_slave_disable(hs);
    device_remove_file(&pdev->dev, &dev_attr_print_level);

    i2c_slave_cdev_exit(hs);

    (void)mutex_destroy(&hs->lock);
    free_irq(hs->irq, hs);
    platform_set_drvdata(pdev, NULL);
    iounmap(hs->regs);

    if (hs->buffer != NULL) {
        /*lint -e648 */
        ClearPageReserved((struct page *)(uintptr_t)(virt_to_page((unsigned long)(uintptr_t)hs->buffer)));
        /*lint +e648 */
        kfree(hs->buffer);
        hs->buffer = NULL;
    }

    kfree(hs);
    hs = NULL;

    return 0;
}

#if defined(CONFIG_OF)
const struct of_device_id i2c_slave_ids[] = {
    { .compatible = "i2c0_slave" },
    { /* sentinel */ }
};

MODULE_DEVICE_TABLE(of, i2c_slave_ids);

#endif

#ifdef CONFIG_PM

STATIC int i2c_slave_suspend(struct device *dev)
{
    unsigned int raw_intr;
    struct platform_device *pdev = container_of(dev, struct platform_device, dev);
    struct i2c_slave_st *hs = platform_get_drvdata(pdev);

    if (hs == NULL) {
        dev_info(&pdev->dev, "get hs faield.\n");
        return 0;
    }

    dev_info(&pdev->dev, "%s:%d, hs=%pK\n", __FUNCTION__, __LINE__, hs);

    if (hs->status == I2C_SLAVE_BUSY) {
        raw_intr = i2c_readl(hs, RAW_INTR_STAT);
        if (I2C_BFEXT(TX_ABRT, raw_intr)) {
            hs->abrt_source = i2c_readl(hs, TX_ABRT_SOURCE);
            (void)i2c_readl(hs, CLR_TX_ABRT);
            hs->stat.tx_abrt++;
            dev_info(&pdev->dev, "tx_abrt=%u, abrt_source=0x%08x on suspend!!!\n", hs->stat.tx_abrt, hs->abrt_source);
        }
        if (I2C_BFEXT(RD_REQ, raw_intr)) {
            dev_info(&pdev->dev, "tx_req on suspend!!!\n");
            (void)i2c_readl(hs, CLR_RD_REQ);
            hs->stat.tx_req++;
            (void)i2c_slave_tx_req(hs);
        }

        i2c_slave_disable(hs);
    }

    return 0;
}

STATIC int i2c_slave_resume(struct device *dev)
{
    struct platform_device *pdev = container_of(dev, struct platform_device, dev);
    struct i2c_slave_st *hs = platform_get_drvdata(pdev);

    if (hs == NULL) {
        dev_info(&pdev->dev, "get hs faield.\n");
        return 0;
    }

    dev_info(&pdev->dev, "%s:%d, hs=%pK\n", __FUNCTION__, __LINE__, hs);

    if (hs->status == I2C_SLAVE_BUSY) {
        i2c_slave_enable(hs);
    }

    return 0;
}

STATIC int i2c_slave_runtime_suspend(struct device *dev)
{
    (void)i2c_slave_suspend(dev);
    return 0;
}

STATIC int i2c_slave_runtime_resume(struct device *dev)
{
    (void)i2c_slave_resume(dev);
    return 0;
}

STATIC const struct dev_pm_ops i2c_slave_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(i2c_slave_suspend,
    i2c_slave_resume) SET_RUNTIME_PM_OPS(i2c_slave_runtime_suspend, i2c_slave_runtime_resume, NULL) };
#define I2C_SLAVE_PM_OPS (&i2c_slave_pm_ops)
#else
#define I2C_SLAVE_PM_OPS NULL
#endif


STATIC struct platform_driver i2c_slave_driver = {
    .driver = {
        .name   = "i2c0_slave",
        .pm = I2C_SLAVE_PM_OPS,
#if defined(CONFIG_OF)
        .of_match_table = of_match_ptr(i2c_slave_ids),
#endif
    },
    .probe      = i2c_slave_probe,
    .remove     = i2c_slave_remove,
};

module_platform_driver(i2c_slave_driver);

MODULE_DESCRIPTION("I2C0 Slave driver");
MODULE_AUTHOR("");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:i2c0_slave");
